<!-- 全局文件上传组件 -->
<template>
  <div class="upload-file-wrapper">
    <a-upload :accept="accept" :multiple="false" :max-count="1" :before-upload="beforeUpload" :custom-request="customRequest" :show-upload-list="false" class="upload-file">
      <div class="upload-item" :class="{ 'upload-item-error': hasError }">
        <div class="upload-icon">
          <slot name="icon">
            <PlusOutlined />
          </slot>
        </div>
        <div class="upload-text">
          {{ displayText }}
        </div>
      </div>
    </a-upload>
  </div>
</template>

<script setup lang="ts">
import { PlusOutlined } from '@ant-design/icons-vue'
import { message } from 'ant-design-vue'
import httpRequest from '@/http/index'

interface Props {
  // 文件类型限制
  accept?: string
  // 最大文件大小 (MB)
  maxSize?: number
  // 自定义显示文本
  text?: string
  // 上传接口地址
  uploadUrl?: string
  // 初始文件列表
}

interface Emits {
  (e: 'success', FileVo: any): void
  (e: 'error', file: UploadFile, error: any): void
  (e: 'remove', file: UploadFile, index: number): void
}

const props = withDefaults(defineProps<Props>(), {
  accept: '*',
  maxSize: 10,
  text: '上传',
  uploadUrl: '/common/upload',
})

const emit = defineEmits<Emits>()

const hasError = ref(false)

// 计算属性
const iconComponent = computed(() => props.icon)
const displayText = computed(() => props.text)

// 文件上传前的校验
const beforeUpload: UploadProps['beforeUpload'] = file => {
  // 文件大小校验
  const isLtMaxSize = file.size / 1024 / 1024 < props.maxSize
  if (!isLtMaxSize) {
    message.error(`文件大小不能超过 ${props.maxSize}MB`)
    return false
  }

  // 文件类型校验
  if (props.accept !== '*') {
    const acceptTypes = props.accept.split(',').map(type => type.trim())
    const fileType = file.type
    const fileName = file.name
    const fileExt = `.${fileName.split('.').pop()?.toLowerCase()}`

    const isAcceptType = acceptTypes.some(type => {
      if (type.startsWith('.')) {
        return fileExt === type.toLowerCase()
      }
      return fileType.includes(type.replace('*', ''))
    })

    if (!isAcceptType) {
      message.error(`只支持上传 ${props.accept} 格式的文件`)
      return false
    }
  }

  hasError.value = false
  return true
}

// 自定义上传请求
const customRequest: UploadProps['customRequest'] = async options => {
  const { file, onSuccess, onError, onProgress } = options
  try {
    // 使用默认上传方法
    const formData = new FormData()
    formData.append('file', file as File)
    // 这里可以使用项目的 httpRequest 方法
    const res = await httpRequest(props.uploadUrl, formData)
    emit('success', res)
  }
  catch (error) {
    hasError.value = true
  }
}

// 格式化文件大小
function formatFileSize(size?: number) {
  if (!size)
    return ''
  if (size < 1024)
    return `${size}B`
  if (size < 1024 * 1024)
    return `${(size / 1024).toFixed(1)}KB`
  return `${(size / 1024 / 1024).toFixed(1)}MB`
}
</script>

<style lang="less" scoped>
.upload-file-wrapper {
  width: 100%;
  height: 100%;

  .upload-file {
    width: 100%;
    height: 100%;

    .upload-item {
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      width: 100%;
      height: 100%;
      cursor: pointer;
      border-radius: 6px;
      transition: all 0.3s;

      &:hover {
        background-color: #f0f8ff;
        border-color: #1890ff;
      }

      &.upload-item-error {
        background-color: #fff2f0;
        border-color: #ff4d4f;
      }

      .upload-icon {
        margin-bottom: 8px;
        font-size: 24px;
        color: #999;

        .upload-item:hover & {
          color: #1890ff;
        }

        .upload-item-error & {
          color: #ff4d4f;
        }
      }

      .upload-text {
        font-size: 14px;
        color: #666;
        text-align: center;

        .upload-item:hover & {
          color: #1890ff;
        }

        .upload-item-error & {
          color: #ff4d4f;
        }
      }
    }
  }

  .file-list {
    margin-top: 16px;

    .file-item {
      display: flex;
      align-items: center;
      justify-content: space-between;
      padding: 8px 12px;
      margin-bottom: 8px;
      background-color: #fff;
      border: 1px solid #d9d9d9;
      border-radius: 6px;

      &.file-item-error {
        background-color: #fff2f0;
        border-color: #ff4d4f;
      }

      .file-info {
        display: flex;
        flex: 1;
        align-items: center;

        .file-icon {
          margin-right: 8px;
          font-size: 16px;
          color: #1890ff;
        }

        .file-name {
          flex: 1;
          margin-right: 8px;
          overflow: hidden;
          font-size: 14px;
          color: #333;
          text-overflow: ellipsis;
          white-space: nowrap;
        }

        .file-size {
          font-size: 12px;
          color: #999;
        }
      }

      .file-actions {
        display: flex;
        gap: 8px;
        align-items: center;

        .loading-icon {
          color: #1890ff;
          animation: spin 1s linear infinite;
        }

        .success-icon {
          color: #52c41a;
        }

        .error-icon {
          color: #ff4d4f;
        }

        .delete-icon {
          color: #999;
          cursor: pointer;

          &:hover {
            color: #ff4d4f;
          }
        }
      }
    }
  }
}

@keyframes spin {
  0% {
    transform: rotate(0deg);
  }

  100% {
    transform: rotate(360deg);
  }
}
</style>
